{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit VirtualPaths;

(*
Notes
=====

This provides a wrapper around our virtual paths for navigating media by tags
for genres, artists, albums, tracks and folders. A string representation of the
path is used which encodes id's for the selected parent items so that we can
treat the path as a standard URL for navigating and cacheing the location.

The following paths are supported:

<empty> = root
folders\ = original filesystem view of media folders
tracks\ = list of all media tracks
albums\ = list of all albums
album.zzz\tracks = list of tracks for album zzz
artists\ = list of all artists
artist.yyy\albums = list of albums for artist yyy
artist.yyy\album.zzz\tracks = list of tracks for artist yyy and album zzz
genres\ = list of all genres
genre.xxx\artists\ = list of artists for genre xxx
genre.xxx\artist.yyy\albums = list of albums for genre xxx and artist yyy
genre.xxx\artist.yyy\album.zzz\tracks = list of tracks for genre xxx,
                                        artist yyy and album zzz.
*)

interface

type
  TAHMVirtualMediaPath = (vmpRoot, vmpGenres, vmpArtists, vmpAlbums, vmpTracks, vmpFolders);

  TAHMVirtualPathManager = class
  private
    FDepth: Integer;
    FAlbumId: Integer;
    FArtistId: Integer;
    FGenreId: Integer;
    FPath: WideString;
    FCaption: String;
    FURL: WideString;
    FCurrent: TAHMVirtualMediaPath;
  protected
    function GetParent: WideString;
    function GetParentCaption: String;
    function GetFolderIndex: WideString;
    procedure SetPath(Path: WideString);
    function ApplyId(Id: Integer; Path: WideString): WideString;
  public
    property Path: WideString read FPath write SetPath;
    property Parent: WideString read GetParent;
    property PathCaption: String read FCaption;
    property ParentCaption: String read GetParentCaption;
    property FolderIndex: WideString read GetFolderIndex;
    property Depth: Integer read FDepth;
    property URL: WideString read FURL;
    property AlbumId: Integer read FAlbumId;
    property ArtistId: Integer read FArtistId;
    property GenreId: Integer read FGenreId;
    property VirtualPath: TAHMVirtualMediaPath read FCurrent;
    function ChangeRoot: WideString;
    function ChangeParent: WideString;
    function ChangeDir(Dir: WideString): WideString;
    function ChangeFolders: WideString;
    function ChangeTracks: WideString;
    function ChangeAlbums: WideString;
    function ChangeArtists: WideString;
    function ChangeGenres: WideString;
    function ChangeAlbum(Id: Integer): WideString;
    function ChangeArtist(Id: Integer): WideString;
    function ChangeGenre(Id: Integer): WideString;
  end;

implementation

uses SysUtils, StrUtils, Unicode;

// TAHMVirtualPathManager

function TAHMVirtualPathManager.GetParent: WideString;
begin
  // Remove selected id from parent folder
  Result := ParentFolderW(FPath);
  if Result <> '' then
    Result := ApplyId(0, Result);
end;

function TAHMVirtualPathManager.GetParentCaption: String;
begin
  // Return just the last folder from parent
  Result := LastFolderW(GetParent);
end;

function TAHMVirtualPathManager.GetFolderIndex: WideString;
var
  i, j: Integer;
begin
  // Return maximum of last two trailing folders
  Result := FPath;
  for i := 1 to FDepth - 2 do
  begin
    j := Pos('\', Result);
    if j > 0 then Delete(Result, 1, j);
  end;
end;

procedure TAHMVirtualPathManager.SetPath(Path: WideString);
var
  i, j: Integer;
  Part, Ext: WideString;
  Context: TAHMVirtualMediaPath;
begin
  // Set default state - clear all cached properties
  FDepth := 0;
  FAlbumId := 0;
  FArtistId := 0;
  FGenreId := 0;
  FPath := '';
  FCaption := '';
  FURL := '';
  FCurrent := vmpRoot;
  Context := vmpRoot;

  // Check for empty string (root) - nothing more to do
  if Path = '' then Exit;

  // Nibble string to see which paths we have
  repeat
    // Parts are delimited by backslash
    i := Pos('\', Path);
    if i = 0 then Break;
    Part := Copy(Path, 1, i - 1);

    // Split extension from path part to parse Id
    Ext := '';
    j := Pos('.', Part);
    if j > 0 then
    begin
      Ext := Copy(Part, j + 1, MaxInt);
      Part := Copy(Part, 1, j - 1);
    end;

    // Remove trailing plural from path part
    if RightStr(Part, 1) = 's' then
      Part := Copy(Part, 1, Length(Part) - 1);

    // Check for supported paths
    if Part = 'Genre' then Context := vmpGenres
    else if Part = 'Artist' then Context := vmpArtists
    else if Part = 'Album' then Context := vmpAlbums
    else if Part = 'Track' then Context := vmpTracks
    else if Part = 'Folder' then Context := vmpFolders
    else Exit;

    // Parse id's if applicable
    case Context of
      vmpGenres: FGenreId := StrToIntDef(Ext, 0);
      vmpArtists: FArtistId := StrToIntDef(Ext, 0);
      vmpAlbums: FAlbumId := StrToIntDef(Ext, 0);
    end;

    // Add this part to cleaned unless its a duplicate
    // in which case we just tweak the id suffix
    if Context <> FCurrent then
    begin
      FPath := FPath + Copy(Path, 1, i);
      FCaption := FCaption + Part + 's\';
      FCurrent := Context;
      Inc(FDepth);
    end
    else FPath := ApplyId(StrToIntDef(Ext, 0), FPath);

    // Remove processed part of path and continue
    Delete(Path, 1, i);

    // Treat anything after folder part as a URL. Quit to avoid name clashes
    if Context = vmpFolders then
    begin
      FURL := Path;
      Exit;
    end;

  until False;
end;

function TAHMVirtualPathManager.ApplyId(Id: Integer; Path: WideString): WideString;
begin
  // Remove trailing backslash, previous Id and plural
  Result := ExcludeTrailingPathDelimiter(Path);
  Result := ChangeFileExt(Result, '');
  if RightStr(Result, 1) = 's' then
    Result := Copy(Result, 1, Length(Result) - 1);

  // Append Id suffix and trailing backslash if required
  if Id = 0 then Result := Result + 's\'
            else Result := Result + '.' + IntToStr(Id) + '\';
end;

function TAHMVirtualPathManager.ChangeRoot: WideString;
begin
  SetPath('');
  Result := FPath;
end;

function TAHMVirtualPathManager.ChangeParent: WideString;
begin
  SetPath(GetParent);
  Result := FPath;
end;

function TAHMVirtualPathManager.ChangeDir(Dir: WideString): WideString;
begin
  if Dir <> '' then
  begin
    // Check for special case parent ".." folder
    if Dir = '..' then ChangeParent
    else if RightStr(Dir, 1) <> '\' then SetPath(FPath + Dir + '\')
                                    else SetPath(FPath + Dir);
  end;
  Result := FPath;
end;

function TAHMVirtualPathManager.ChangeFolders: WideString;
begin
  SetPath('Folders\');
  Result := FPath;
end;

function TAHMVirtualPathManager.ChangeTracks: WideString;
begin
  SetPath('Tracks\');
  Result := FPath;
end;

function TAHMVirtualPathManager.ChangeAlbums: WideString;
begin
  SetPath('Albums\');
  Result := FPath;
end;

function TAHMVirtualPathManager.ChangeArtists: WideString;
begin
  SetPath('Artists\');
  Result := FPath;
end;

function TAHMVirtualPathManager.ChangeGenres: WideString;
begin
  SetPath('Genres\');
  Result := FPath;
end;

function TAHMVirtualPathManager.ChangeAlbum(Id: Integer): WideString;
begin
  FPath := ApplyId(Id, FPath);
  Result := ChangeDir('Tracks\');
end;

function TAHMVirtualPathManager.ChangeArtist(Id: Integer): WideString;
begin
  FPath := ApplyId(Id, FPath);
  Result := ChangeDir('Albums\');
end;

function TAHMVirtualPathManager.ChangeGenre(Id: Integer): WideString;
begin
  FPath := ApplyId(Id, FPath);
  Result := ChangeDir('Artists\');
end;


end.
